package com.sudo.service.admin.service.impl;

import com.github.pagehelper.PageHelper;
import com.sudo.common.enums.SysJobStatus;
import com.sudo.common.utils.PagedGridResult;
import com.sudo.common.utils.ThreadLocalUtil;
import com.sudo.dao.admin.entity.SysServerNodeJob;
import com.sudo.dao.admin.mapper.SysServerNodeJobMapper;
import com.sudo.dao.admin.model.bo.SysRunServerNodeJobBO;
import com.sudo.dao.admin.model.bo.SysServerNodeJobBO;
import com.sudo.rpc.api.model.CallTask;
import com.sudo.rpc.api.service.RmiSetServerService;
import com.sudo.rpc.api.utils.RpcRes;
import com.sudo.service.admin.config.CronTaskRegistry;
import com.sudo.service.admin.runnable.RpcMethodRunnable;
import com.sudo.service.admin.service.AdminBaseService;
import com.sudo.service.admin.service.SysServerNodeJobService;
import com.sudo.service.tldata.ThreadLocalObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@Service
public class SysServerNodeJobServiceImpl extends AdminBaseService implements SysServerNodeJobService {

    @Resource
    private SysServerNodeJobMapper sysServerNodeJobMapper;

    @Resource
    private CronTaskRegistry cronTaskRegistry;

    @Resource
    private RmiSetServerService rmiSetServerService;

    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public PagedGridResult querySysServerNodeJobByPage(SysServerNodeJobBO sysServerNodeJobBO) {
        PageHelper.startPage(sysServerNodeJobBO.getPage(), sysServerNodeJobBO.getPageSize());
        Example example = new Example(SysServerNodeJob.class);
        example.orderBy("id").desc();
        Example.Criteria criteria = example.createCriteria();
        if (StringUtils.isNotBlank(sysServerNodeJobBO.getServer())) {
            criteria.andEqualTo("server", sysServerNodeJobBO.getServer());
        }
        if (StringUtils.isNotBlank(sysServerNodeJobBO.getBeanName())) {
            criteria.andEqualTo("beanName", sysServerNodeJobBO.getBeanName());
        }
        if (StringUtils.isNotBlank(sysServerNodeJobBO.getMethodName())) {
            criteria.andEqualTo("methodName", sysServerNodeJobBO.getMethodName());
        }
        if (StringUtils.isNotBlank(sysServerNodeJobBO.getMethodParams())) {
            criteria.andLike("methodParams", "%"+sysServerNodeJobBO.getMethodParams()+"%");
        }
        if (StringUtils.isNotBlank(sysServerNodeJobBO.getStatus())) {
            criteria.andEqualTo("status", sysServerNodeJobBO.getStatus());
        }
        List<SysServerNodeJob> sysServerNodeJobList = sysServerNodeJobMapper.selectByExample(example);
        return setterPagedGrid(sysServerNodeJobList, sysServerNodeJobBO.getPage());
    }


    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    public List<SysServerNodeJob> getSysServerNodeJobByStatus(String status) {
        Example example = new Example(SysServerNodeJob.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("status", status);
        return sysServerNodeJobMapper.selectByExample(example);
    }

    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public SysServerNodeJob querySysServerNodeJobById(Integer id) {
        return sysServerNodeJobMapper.selectByPrimaryKey(id);
    }

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int updateSysServerNodeJobStatus(SysServerNodeJobBO SysServerNodeJobBO) {
        SysServerNodeJob sysServerNodeJob = new SysServerNodeJob();
        sysServerNodeJob.setId(SysServerNodeJobBO.getId());
        sysServerNodeJob.setStatus(SysServerNodeJobBO.getStatus());
        if (!SysJobStatus.RUN.name().equals(sysServerNodeJob.getStatus())
                && !SysJobStatus.STOP.name().equals(sysServerNodeJob.getStatus())){
            sysServerNodeJob.setStatus(SysJobStatus.STOP.name());
        }
        int affectCount = 0;
        //修改SysServerNodeJob时，判断修改之前的状态，如果状态不一致，则需要关闭或打开正在内存中的定时任务
        SysServerNodeJob oldSysServerNodeJob = sysServerNodeJobMapper.selectByPrimaryKey(SysServerNodeJobBO.getId());
        if (!oldSysServerNodeJob.getStatus().equals(sysServerNodeJob.getStatus())){

            affectCount = sysServerNodeJobMapper.updateByPrimaryKeySelective(sysServerNodeJob);

            if (affectCount > 0) {
                //删除old Task
                RpcMethodRunnable rpcMethodRunnable = new RpcMethodRunnable(oldSysServerNodeJob);
                cronTaskRegistry.removeCronTask(rpcMethodRunnable);
                //打开
                if (SysJobStatus.RUN.name().equals(sysServerNodeJob.getStatus())) {
                    cronTaskRegistry.addCronTask(rpcMethodRunnable,oldSysServerNodeJob.getCronExpression());
                }
            }
        }
        return affectCount;
    }

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int saveSysServerNodeJob(SysServerNodeJobBO SysServerNodeJobBO, String sessionUser) {
        SysServerNodeJob sysServerNodeJob = new SysServerNodeJob();
        BeanUtils.copyProperties(SysServerNodeJobBO, sysServerNodeJob);
        sysServerNodeJob.setStatus(SysJobStatus.STOP.name());
        LocalDateTime localDateTime = LocalDateTime.now();
        sysServerNodeJob.setCreateTime(localDateTime);
        sysServerNodeJob.setUpdateTime(localDateTime);
        return sysServerNodeJobMapper.insertSelective(sysServerNodeJob);
    }


    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int updateSysServerNodeJobWithNoStatus(SysServerNodeJobBO SysServerNodeJobBO) {
        SysServerNodeJob sysServerNodeJob = new SysServerNodeJob();
        BeanUtils.copyProperties(SysServerNodeJobBO,sysServerNodeJob);
        if (!SysJobStatus.RUN.name().equals(sysServerNodeJob.getStatus())
                && !SysJobStatus.STOP.name().equals(sysServerNodeJob.getStatus())){
            sysServerNodeJob.setStatus(SysJobStatus.STOP.name());
        }
        sysServerNodeJob.setBeanName(null);
        sysServerNodeJob.setMethodName(null);
        sysServerNodeJob.setStatus(null);
        sysServerNodeJob.setUpdateTime(LocalDateTime.now());

        return sysServerNodeJobMapper.updateByPrimaryKeySelective(sysServerNodeJob);
    }

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int deleteSysServerNodeJob(Integer id) {
        SysServerNodeJob sysServerNodeJob = sysServerNodeJobMapper.selectByPrimaryKey(id);
        int affectCount = 0;
        if (sysServerNodeJob != null) {
            affectCount = sysServerNodeJobMapper.deleteByPrimaryKey(id);
            if (affectCount > 0) {
                //删除old Task
                try {
                    RpcMethodRunnable rpcMethodRunnable = new RpcMethodRunnable(
                            sysServerNodeJob.getId(),
                            sysServerNodeJob.getServer(),
                            sysServerNodeJob.getBeanName(),
                            sysServerNodeJob.getMethodName()
                    );
                    cronTaskRegistry.removeCronTask(rpcMethodRunnable);
                }catch (NoSuchBeanDefinitionException e){
                    e.printStackTrace();
                }
            }
        }
        return affectCount;
    }


    @Override
    public RpcRes callTaskMethod(SysServerNodeJob sysServerNodeJob, SysRunServerNodeJobBO sysRunServerNodeJobBO) throws Exception {
        sysServerNodeJob.setMethodParams(sysRunServerNodeJobBO.getMethodParams());
        Map<String, Object> jobConfigMap = ThreadLocalObject.initJobConfigMap(sysServerNodeJob);
        ThreadLocalUtil.setTlMap(jobConfigMap);
        String[] split = sysServerNodeJob.getServer().split(":");
        CallTask callTask = new CallTask(split[0], Integer.parseInt(split[1]),
                sysServerNodeJob.getBeanName(), sysServerNodeJob.getMethodName()
                ,sysRunServerNodeJobBO.getMethodParams(),sysRunServerNodeJobBO.getCallbackFun());

        return (RpcRes)rmiSetServerService.callServer(callTask);
    }
}
